#include "depui/depui.h"
#include <string.h>

#define MXMODULE_LIST

static void mx__gfxsel_sizes(MX_GFXSEL * sel)
{
	MX_LISTELEM *ptr;
	const MX_PLATFORM_RESOLUTION *r = 0;
	const MX_PLATFORM_DEPTH *c = 0;
	const MX_PLATFORM_DRIVER *d = 0;
	const MX_PLATFORM_THEME *t = 0;

	MXDEBUG_INVARIANT(MXOBJ(sel));

	sel->_driverw = -1;
	sel->_resw = -1;
	sel->_depthw = -1;

	mx_platform_modes(&d, &r, &c, &t);
	if (d) {
		int maxw = 0;

		ptr = mx_list_first(&sel->_driver);
		while (ptr) {
			int w;
			MX_RECT rrect;

			mx_defaultrect(ptr, &rrect);
			w = rrect.x2 - rrect.x1;

			maxw = MX_MAX(maxw, w + 10);

			ptr = mx_list_next(ptr);
		}
		sel->_driverw = maxw;
	}

	if (r) {
		int maxw = 0;

		ptr = mx_list_first(&sel->_res);
		while (ptr) {
			int w;
			MX_RECT rrect;

			mx_defaultrect(ptr, &rrect);
			w = rrect.x2 - rrect.x1;

			maxw = MX_MAX(maxw, w + 10);

			ptr = mx_list_next(ptr);
		}
		sel->_resw = maxw;
	}

	if (c) {
		int maxw = 0;

		ptr = mx_list_first(&sel->_depth);
		while (ptr) {
			int w;
			MX_RECT rrect;

			mx_defaultrect(ptr, &rrect);
			w = rrect.x2 - rrect.x1;

			maxw = MX_MAX(maxw, w + 10);

			ptr = mx_list_next(ptr);
		}
		sel->_depthw = maxw;
	}
}

static void mx__gfxsel_geometry(MX_GFXSEL * sel)
{
	int x;
	int w = mx_w(sel) - 2;
	int h = mx_h(sel);
	const int listw = sel->_resw + sel->_depthw + sel->_driverw;

	mx_defaultrect(&sel->_ok, 0);
	mx_defaultrect(&sel->_apply, 0);
	mx_geometry(&sel->_apply);
	mx_geometry(&sel->_ok);

	h -= mx_h(&sel->_ok) + 1;

	mx_position(&sel->_driver, 0, 0, (sel->_driverw * w) / listw, h);
	mx_geometry(&sel->_driver);

	x = mx_x(&sel->_driver) + mx_w(&sel->_driver) + 2;
	mx_position(&sel->_res, x, 0, (sel->_resw * w) / listw, h);
	mx_geometry(&sel->_res);

	x = mx_x(&sel->_res) + mx_w(&sel->_res) + 2;
	mx_position(&sel->_depth, x, 0, (sel->_depthw * w) / listw, h);
	mx_geometry(&sel->_depth);

	mx_layout(&sel->_ok, (MX_LAYOUT_X2 | MX_LAYOUT_Y2), sel, 0, 0);
	mx_layout(&sel->_apply, (MX_LAYOUT_LEFT | MX_LAYOUT_Y1), &sel->_ok, 2, 0);
}

static void mx__gfxsel_apply(MX_GFXSEL * sel)
{
	MX_GFXSEL_INFO info;

	const MX_PLATFORM_RESOLUTION *r = 0;
	const MX_PLATFORM_DEPTH *c = 0;
	const MX_PLATFORM_DRIVER *d = 0;
	const MX_PLATFORM_THEME *t = 0;
	const int rsel = mx_list_selected_id(&sel->_res);
	const int csel = mx_list_selected_id(&sel->_depth);
	const int dsel = mx_list_selected_id(&sel->_driver);

	memset(&info, 0, sizeof(info));

	mx_platform_modes(&d, &r, &c, &t);

	if ((r) && (rsel >= 0)) {
		info.w = r[rsel].w;
		info.h = r[rsel].h;
	}

	if ((c) && (csel >= 0))
		info.c = c[csel].c;

	if ((c) && (dsel >= 0))
		info.driver = d[dsel].driver;

	if (mx_inform(MX_GFXSEL_OK, &info, sizeof(info)) == 0) {

		if ((csel >= 0) || (dsel >= 0) || (rsel >= 0)) {
			mx_platform_start(info.w, info.h, info.c, info.driver);
			mx_platform_dirty(mx_platform_rect());
		}
	}
}

void mx_gfxsel_handler(MX_WIN * win)
{
	MX_GFXSEL *sel = (MX_GFXSEL *) win;

	MXDEBUG_INVARIANT(MXOBJ(win));
	MXDEBUG_INVARIANT(MXOBJ(mx.obj));

	/* Handle gfxsel events */
	if (mx_eventmatch(MX_GEOMETRY, sel)) {
		mx_win_handler(win);
		mx__gfxsel_geometry(sel);
		return;

	} else if (mx_eventmatch(MX_THEME_CHANGE, sel)) {
		mx__gfxsel_sizes(sel);

	} else if (mx_eventmatch(MX_DEFAULTRECT, sel)) {
		MX_RECT *rrect = mx_defaultrect_data();

		rrect->x2 = rrect->x1 + sel->_resw + sel->_depthw + sel->_driverw + 2;
		rrect->y2 = rrect->y1 + 160;
		return;

		/**module Pressing the OK button sends a MX_GFXSEL_OK event to the parent
        window and immediatly destroys the graphics mode window. */
	} else if (mx_eventmatch(MX_SELECT, &sel->_ok)) {
		mx__gfxsel_apply(sel);
		mx_delete(sel);
		return;

		/**module Pressing the APPLY button sends a MX_GFXSEL_OK event to the
        parent window. */
	} else if (mx_eventmatch(MX_SELECT, &sel->_apply)) {
		mx__gfxsel_apply(sel);
		return;

	} else if (mx_eventmatch(MX_KEY_UNUSED, win)) {
		const MX_KEY_INFO *info = mx_key_info();

		if ((info) && (info->ascii == 0x1b)) {
			mx_win_dirty(sel);
			mx_delete(sel);
			return;
		}
	}

	mx_win_handler(win);
}

MX_GFXSEL *mx_gfxselwin(MX_GFXSEL * sel, size_t size, MX_HANDLER handler, int theid)
{
	MXMINSIZE(size, MX_GFXSEL);

	if (!handler)
		handler = mx_gfxsel_handler;

	sel = (MX_GFXSEL *) mx_win(sel, size, handler, theid);
	if (sel) {
		const MX_PLATFORM_RESOLUTION *r = 0;
		const MX_PLATFORM_DEPTH *c = 0;
		const MX_PLATFORM_DRIVER *d = 0;
		const MX_PLATFORM_THEME *t = 0;

		MXDEBUG_ALLOCTAG(sel);
		MXDEBUG_ATOMNAME(sel, "gfxsel");
		mx_text_set(sel, "Choose GFXmode...", -1, 0);

		mx_button(&sel->_ok, 0, sel, 0);
		mx_text_set(&sel->_ok, "ok", -1, 0);
		MXDEBUG_ATOMNAME(&sel->_ok, "gfxsel_ok");

		mx_button(&sel->_apply, 0, sel, 0);
		mx_text_set(&sel->_apply, "apply", -1, 0);
		MXDEBUG_ATOMNAME(&sel->_apply, "gfxsel_ok");

		mx_list(&sel->_res, 0, sel, 0);
/**fix		mx_text_set(&sel->_res, "Resolution", -1, 0); */
		MXDEBUG_ATOMNAME(&sel->_res, "gfxsel_res");

		mx_list(&sel->_depth, 0, sel, 0);
/**fix		mx_text_set(&sel->_depth, "Depth", -1, 0); */
		MXDEBUG_ATOMNAME(&sel->_depth, "gfxsel_depth");

		mx_list(&sel->_driver, 0, sel, 0);
/**fix		mx_text_set(&sel->_driver, "Driver", -1, 0); */
		MXDEBUG_ATOMNAME(&sel->_driver, "gfxsel_driver");

		/* Fill each of the lists with the sel mode info */
		mx_platform_modes(&d, &r, &c, &t);
		if (d) {
			int num = 0;
			const MX_PLATFORM_DRIVER *ptr = d;

			while (ptr->text) {
				MX_LISTELEM *elem = mx_listelem((MX_LISTELEM *) 0, 0, &sel->_driver, num);

				mx_text_set(elem, ptr->text, -1, 0);
				mx_defaultrect(elem, 0);
				++ptr;
				++num;
			}
		}
		if (r) {
			int num = 0;
			const MX_PLATFORM_RESOLUTION *ptr = r;

			while (ptr->text) {
				MX_LISTELEM *elem = mx_listelem((MX_LISTELEM *) 0, 0, &sel->_res, num);

				mx_text_set(elem, ptr->text, -1, 0);
				mx_defaultrect(elem, 0);
				++ptr;
				++num;
			}
		}
		if (c) {
			int num = 0;
			const MX_PLATFORM_DEPTH *ptr = c;

			while (ptr->text) {
				MX_LISTELEM *elem = mx_listelem((MX_LISTELEM *) 0, 0, &sel->_depth, num);

				mx_text_set(elem, ptr->text, -1, 0);
				mx_defaultrect(elem, 0);
				++ptr;
				++num;
			}
		}

		mx__gfxsel_sizes(sel);
	}

	MXDEBUG_INVARIANT(MXOBJ(sel));
	return sel;
}

/** When handling a MX_GFXSEL_OK event this function can be called to
get information about the graphics mode selected.  */
MX_GFXSEL_INFO *mx_gfxsel_info(const int idnum)
{
	MX_GFXSEL_INFO *sel;

/** This function should only be called while handling a MX_GFXSEL_OK event. */
	assert(mx.event == MX_GFXSEL_OK);

	if (mx.event != MX_GFXSEL_OK)
		return 0;

	sel = (MX_GFXSEL_INFO *) mx.data;
	assert(sel);

/** The specified id number must match the one of the gfxsel selector. */
	if ((sel) && (sel->idnum == idnum))
		return sel;

	return 0;
}
